/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#include "DataSerializer.h"

#include "NativeFile.h"

namespace PQCore
{
	int SerializeDatagram::_count=0;
	//for output
	SerializeDatagram::SerializeDatagram(int bodyLength)//count is body length
		: _data(0),_pos(0)
	{
		_data=new char[bodyLength+HEADER_LENGTH];
		*(int*)_data=_count;//datagram id
		_pos+=sizeof(int);

		*(int*)(_data+_pos)=bodyLength;//header is composed by id and bodylength
		_pos+=sizeof(int);

		_count++;
	}

	//for input data already contain id body length
	//contain all data
	SerializeDatagram::SerializeDatagram(char* data,int count)
		: _data(0),_pos(0)
	{
		_pos=0;
		_pos+=sizeof(int);
		_pos+=sizeof(int);
		_data=new char[count];
		memcpy(_data,data,count);
	}

	int SerializeDatagram::getId()
	{
		int id=*(int*)_data;
		return id;
	}

	SerializeDatagram::~SerializeDatagram()
	{
		if(_data){
			delete[] _data;
		}
	}


	char* SerializeDatagram::data()
	{
		return _data;
	}

	int SerializeDatagram::length()
	{
		int len=HEADER_LENGTH + *(int*)(_data+sizeof(int));
		return len;
	}

	char* SerializeDatagram::body()
	{
		return _data + HEADER_LENGTH;
	}

	void SerializeDatagram::write(int i)
	{
		(*(int*)(_data+_pos))=i;
		_pos+=sizeof(i);
	}

	void SerializeDatagram::write(float f)
	{
		(*(float*)(_data+_pos))=f;
		_pos+=sizeof(f);
	}

	void SerializeDatagram::write(char* buf,int count)
	{
		memcpy(_data+_pos,buf,count);
		_pos+=count;
	}

	bool SerializeDatagram::readBool()
	{
		int i=*(int*)(_data+_pos);
		_pos+=sizeof(int);
		if(i==0){
			return false;
		}
		return true;;
	}

	int SerializeDatagram::readInt()
	{
		int i=*(int*)(_data+_pos);
		_pos+=sizeof(int);
		return i;
	}

	float SerializeDatagram::readFloat()
	{
		float f=*(float*)(_data+_pos);
		_pos+=sizeof(float);
		return f;
	}

	double SerializeDatagram::readDouble()
	{
		double d=*(double*)(_data+_pos);
		_pos+=sizeof(double);
		return d;
	}

	std::string SerializeDatagram::readString(int bytes)
	{
		std::string str(_data+_pos,bytes);
		_pos+=bytes;
		return str;
	}

	char* SerializeDatagram::readBytes(int bytes)
	{
		char* buf=(_data+_pos);
		_pos+=bytes;
		return buf;
	}

	//decode header,get body length in bytes
	int SerializeDatagram::getBodyLength()
	{
		int len=*(int*)(_data+sizeof(int));
		return len;
	}

	SerializeDataParam::SerializeDataParam(bool b)
	{
		_type=SerializeDataType_BOOL;
		_count=sizeof(int);
		_data=new char[_count];
		int i=0;
		if(b){
			i=1;
		}
		(*(int*)_data)=i;
		
	}

	SerializeDataParam::SerializeDataParam(int i)
	{
		_type=SerializeDataType_INT;
		_count=sizeof(i);
		_data=new char[_count];
		(*(int*)_data)=i;
		
	}
	
	SerializeDataParam::SerializeDataParam(float f)
	{
		_type=SerializeDataType_FLOAT;
		_count=sizeof(f);
		_data=new char[_count];
		(*(float*)_data)=f;
	}

	SerializeDataParam::SerializeDataParam(double d)
	{
		_type=SerializeDataType_DOUBLE;
		_count=sizeof(d);
		_data=new char[_count];
		(*(double*)_data)=d;
	}

	SerializeDataParam::SerializeDataParam(std::string str)
	{
		_type=SerializeDataType_STRING;
		_count=str.length();
		_data=new char[_count];
		memcpy(_data,str.data(),_count);
	}


	char* SerializeDataParam::getData()
	{
		return _data;
	}
	

	SerializeDataParam::~SerializeDataParam()
	{
		if(_data){
			delete[] _data;
		}
	}

	std::string SerializeDataParam::getString()
	{
		assert(_type==SerializeDataType_STRING);
		std::string str(_data,_count);
		return str;
	}

	SerializeDataParam::SerializeDataParam(char* bytes,int count)
	{
		_type=SerializeDataType_BYTES;
		_count=count;
		_data=new char[_count];
		memcpy(_data,bytes,_count);
	}

	SerializeDataType SerializeDataParam::getParamType()
	{
		return _type;
	}
	
	bool SerializeDataParam::getBool()
	{
		assert(_type==SerializeDataType_BOOL);
		int ret= (*(int*)_data);
		if(ret==0){
			return false;
		}else{
			return true;
		}
	}

	int SerializeDataParam::getInt()
	{
		assert(_type==SerializeDataType_INT);
		return (*(int*)_data);
	}

	float SerializeDataParam::getFloat()
	{
		assert(_type==SerializeDataType_FLOAT);
		return (*(float*)_data);
	}

	int SerializeDataParam::getByteCount()
	{
		return _count;
	}

	SerializeParser::~SerializeParser()
	{
		for(int i=0;i<_paramCount;i++){
			delete _paramList[i];
		}
	}

	SerializeDataId SerializeParser::getName()
	{
		return _name;
	}

	SerializeDataParam* SerializeParser::getParam(int index)
	{
		if(index<0||index>=_paramCount){
			return 0;
		}else{
			return _paramList.at(index);
		}
		
	}

	SerializeDataParam* SerializeParser::getParam()
	{
		if(_index>=0&&_index<_paramCount){
			return _paramList[_index++];
		}else{
			return 0;
		}
	}

	SerializeParser::SerializeParser(std::string fileName)
		: _index(0),_datagram(0),_name(SerializeDataId_Unknown),_paramCount(0)
	{
		if(!NativeFile::isExist(fileName)){
			_success=false;
			return;
		}
		_datagram=NativeFile::readDatagram(fileName);

		if(_datagram){
			parse();
			_success=true;
		}else{
			_success=false;
		}

		delete _datagram;//delete self create
	}


	SerializeParser::SerializeParser(SerializeDatagram* datagram)
		: _index(0),_datagram(datagram),_name(SerializeDataId_Unknown),_paramCount(0)
	{
		if(_datagram){
			parse();
			_success=true;
		}else{
			_success=false;
		}
		//
	}

	void SerializeParser::parse()
	{
		//read param count(int)		
		_paramCount=_datagram->readInt();

		//read header(bytes)
		_name=(SerializeDataId)_datagram->readInt();
		for (int i = 0; i < _paramCount; i++){
			SerializeDataType type=(SerializeDataType)_datagram->readInt();
			int _count=_datagram->readInt();
			paramTypeList.push_back(type);
			paramByteCountList.push_back(_count);
		}

		//read body(param content)
		for (int i = 0; i < _paramCount; i++){
			SerializeDataType type=paramTypeList.at(i);
			switch(type){
				case SerializeDataType_BOOL:{
					bool b=_datagram->readBool();
					SerializeDataParam *param=new SerializeDataParam(b);
					_paramList.push_back(param);
					break;
				}
				case SerializeDataType_INT:{
					int i=_datagram->readInt();
					SerializeDataParam *param=new SerializeDataParam(i);
					_paramList.push_back(param);
					break;
				}
				
				case SerializeDataType_FLOAT:{
					float f=_datagram->readFloat();
					SerializeDataParam *param=new SerializeDataParam(f);
					_paramList.push_back(param);
					break;
				}
				case SerializeDataType_DOUBLE:{
					double d=_datagram->readDouble();
					SerializeDataParam *param=new SerializeDataParam(d);
					_paramList.push_back(param);
					break;
				}

				case SerializeDataType_STRING:{
					std::string str=_datagram->readString(paramByteCountList.at(i));
					SerializeDataParam *param=new SerializeDataParam(str);
					_paramList.push_back(param);
					break;
				}
				case SerializeDataType_BYTES:
				default: {
					int _count=paramByteCountList.at(i);
					char* buf=_datagram->readBytes(_count);
					SerializeDataParam *param=new SerializeDataParam(buf,_count);
					_paramList.push_back(param);
					break;
				}
			}
		}
	}


	SerializeGenerator::SerializeGenerator(SerializeDataId name)
	{
		_name = name;
		_paramCount=0;
		_length=sizeof(_name);//content length
		_length+=sizeof(_paramCount);
		_datagram=0;
		
	}

	SerializeGenerator::~SerializeGenerator()
	{
		int size=_paramList.size();
		for(int i=0;i<size;i++){
			delete _paramList[i];
		}
	}

	void SerializeGenerator::addBoolParam(bool b)
	{
		_paramList.push_back(new SerializeDataParam(b));
		_length+=sizeof(int);//this is for param type(type is identified by int) 
		_length+=sizeof(int);//this is for param length(in byte),can be earase
		_length+=sizeof(int);//bool stored as int

		_paramCount++;
	}

	void SerializeGenerator::addIntParam(int i)
	{
		_paramList.push_back(new SerializeDataParam(i));
		_length+=sizeof(int);//this is for param type(type is identified by int) 
		_length+=sizeof(int);//this is for param length(in byte),can be earase
		_length+=sizeof(i);

		_paramCount++;
	}

	void SerializeGenerator::addFloatParam(float f)
	{
		_paramList.push_back(new SerializeDataParam(f));
		_length+=sizeof(int);//this is for param type(type is identified by int) 
		_length+=sizeof(int);//this is for param length(in byte),can be earase
		_length+=sizeof(f);

		_paramCount++;
	}

	void SerializeGenerator::addDoubleParam(double d)
	{
		_paramList.push_back(new SerializeDataParam(d));
		_length+=sizeof(int);//this is for param type(type is identified by int) 
		_length+=sizeof(int);//this is for param length(in byte),can be earase
		_length+=sizeof(d);

		_paramCount++;
	}


	void SerializeGenerator::addStringParam(std::string str)
	{
		_paramList.push_back(new SerializeDataParam(str));

		_length+=sizeof(int);//this is for param type(type is identified by int) 
		_length+=sizeof(int);//this is for param length(in byte),can be earase
		_length+=str.length();

		_paramCount++;
	}

	void SerializeGenerator::addBytesParam(char *data,int count)
	{        
		_paramList.push_back(new SerializeDataParam(data,count));

		_length+=sizeof(int);//this is for param type(type is identified by int) 
		_length+=sizeof(int);//this is for param length(in byte),can be earase
		_length+=count;

		_paramCount++;
	}

	SerializeDatagram* SerializeGenerator::genDatagram()
	{
		_datagram=new SerializeDatagram(_length);
		//write param count(int)		
		_datagram->write(_paramCount);
		//generate header(bytes)
		_datagram->write(_name);//
		for (int i = 0; i < _paramCount; i++){
			 _datagram->write(_paramList[i]->getParamType());
			 _datagram->write(_paramList[i]->getByteCount());        
		}
		//generate body(param content)
		for (int i = 0; i < _paramCount; i++){
			_datagram->write(_paramList[i]->getData(),_paramList[i]->getByteCount());
		}

		return _datagram;
	}

	void SerializeGenerator::serialize(std::string fileName)
	{
		SerializeDatagram* dgram=genDatagram();
		NativeFile::write(fileName,dgram);
		delete dgram;
	}

}
